package com.jayson.mymvc.servlet;

import com.jayson.mymvc.annotations.*;
import com.jayson.mymvc.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

public class MyServlet extends HttpServlet {

    // 保存配置信息
    private Properties properties = new Properties();

    // 保存包内的所有类名
    private List<String> classNames = new ArrayList<>();

    // 保存初始化的所有bean
    private Map<String, Object> beanMap = new HashMap<>();

    // 保存所有方法的处理器
    private static List<Handler> handlers = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getRequestURI();
        Handler handler = getHandler(url);



        if(handler == null) {
            resp.getWriter().write("404 not found");
            return;
        }
        // 校验用户名是否有访问权限
        String userName = req.getParameter("username");
        if(handler.getUserSet().size() > 0 && !handler.getUserSet().contains(userName)){
            resp.setHeader("Content-type", "text/html;charset=UTF-8");
            resp.getWriter().write("对不起，你无权访问该地址");
            return;
        }
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

        Object[] paramValues = new Object[parameterTypes.length];

        Map<String, String[]> parameterMap = req.getParameterMap();
        for(String key : parameterMap.keySet()){
            String[] values = parameterMap.get(key);
            String valueStr = StringUtils.join(values);
            Integer index = handler.getParamIndexMapping().get(key);
            if(null != index) {
                paramValues[index] = valueStr;
            }
        }

        Integer reqIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        if(null != reqIndex){
            paramValues[reqIndex] = req;
        }
        Integer respIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        if(null != respIndex){
            paramValues[respIndex] = resp;
        }

        try {
            handler.getMethod().invoke(handler.getController(), paramValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    public static Handler getHandler(String url) {
        for (Handler handler : handlers) {
            if(url.equals(handler.getUrl())){
                return handler;
            }
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 加载配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);

        // 扫描包内的所有类（基于注解）
        doScan(properties.getProperty("scanPackage"));

        // 初始化bean对象
        doInstance();

        // 注入属性
        doAutowired();

        // 构造HandleMapping处理器
        initHandleMapping();
        System.out.println("mymvc 初始化完成......");
    }

    /**
     * 构造HandleMapping处理器，将方法和URL对应
     */
    private void initHandleMapping() {
        for(String key : beanMap.keySet()){
            Object obj = beanMap.get(key);
            Class<?> aClass = obj.getClass();
            if(!aClass.isAnnotationPresent(Controller.class)) continue;

            String baseUrl = "";
            // 获取controller类上的requestMapping路径
            if(aClass.isAnnotationPresent(RequestMapping.class)){
                RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                baseUrl += annotation.value();
            }

            // 获取controller类上的Security注解的值，即是拥有访问该类方法权限的用户名
            Set<String> classUserNames = new HashSet<>();
            if(aClass.isAnnotationPresent(Security.class)) {
                Security classAnnotation = aClass.getAnnotation(Security.class);
                String[] strings = classAnnotation.value();
                classUserNames.addAll(new ArrayList<>(Arrays.asList(strings)));
            }

            // 获取controller类里的方法的requestMapping路径
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if(!method.isAnnotationPresent(RequestMapping.class)) continue;

                RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                String methodUrl = baseUrl + annotation.value();

                Handler handler = new Handler(obj, method, methodUrl);

                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    if(parameter.getType() == HttpServletRequest.class ||
                        parameter.getType() == HttpServletResponse.class){
                        handler.getParamIndexMapping().put(parameter.getType().getSimpleName(), i);
                    } else {
                        handler.getParamIndexMapping().put(parameter.getName(), i);
                    }
                }

                // 获取方法的Security注解的值，即是单独拥有访问该方法权限的用户名
                if(method.isAnnotationPresent(Security.class)){
                    Security methodAnnotation = method.getAnnotation(Security.class);
                    String[] methodUserNames = methodAnnotation.value();

                    handler.getUserSet().addAll(classUserNames);
                    handler.getUserSet().addAll(new ArrayList<>(Arrays.asList(methodUserNames)));
                }


                handlers.add(handler);
            }
        }

    }

    /**
     * 为bean的属性注入
     */
    private void doAutowired() {
        for(String key : beanMap.keySet()){
            Object obj = beanMap.get(key);
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                if(!field.isAnnotationPresent(Autowired.class)) continue;

                Autowired annotation = field.getAnnotation(Autowired.class);
                String name = annotation.value();
                if(StringUtils.isBlank(name)){
                    name = field.getType().getName();
                }

                field.setAccessible(true);
                try {
                    field.set(obj, beanMap.get(name));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 初始化bean对象
     */
    private void doInstance() {
        try {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                if (aClass.isAnnotationPresent(Controller.class)) {
                    String key = lowerFirst(aClass.getSimpleName());
                    Object obj = aClass.getDeclaredConstructor().newInstance();
                    beanMap.put(key, obj);
                } else if (aClass.isAnnotationPresent(Service.class)) {
                    Service annotation = aClass.getAnnotation(Service.class);
                    String key = annotation.value();
                    if (StringUtils.isBlank(key)) {
                        key = lowerFirst(aClass.getSimpleName());
                    }
                    Object obj = aClass.getDeclaredConstructor().newInstance();
                    beanMap.put(key, obj);

                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        String interfaceName = anInterface.getName();
                        beanMap.put(interfaceName, obj);
                    }
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 首字母小写
     * @param simpleName
     * @return
     */
    private String lowerFirst(String simpleName) {
        char c = simpleName.charAt(0);
        String temp = String.valueOf(c);
        return simpleName.replaceFirst(temp, temp.toLowerCase());
    }

    /**
     *  扫描包内的所有类（基于注解）
     * @param scanPackage
     */
    private void doScan(String scanPackage) {
        String packagePath = scanPackage.replaceAll("\\.", "/");
        String filePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + "/" + packagePath;
        File rootFile = new File(filePath);

        File[] files = rootFile.listFiles();
        for (File file : files) {
            if(file.isDirectory()){
                doScan(scanPackage + "." + file.getName());
            } else {
                String className = file.getName().substring(0, file.getName().lastIndexOf("."));
                classNames.add(scanPackage + "." + className);
            }
        }
    }

    /**
     * 加载配置文件
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
